双向循环链表的实现
-
目标
- 使用Linux内核链表实现KylinLib中的双向循环链表
template<typename T>class DualCircleList;
-
KylinLib中双向循环链表的设计思路
数据结点之间在逻辑上构成双向循环链表,头结点仅用于结点的定位。
-
实现思路
- 通过模板定义DualCircleList类,继承自DualLinkList类
- 在DualCircleList内部使用Linux内核链表进行实现
- 使用struct list_head定义DualCircleList的头结点
- 特殊处理:循环遍历时忽略头结点
-
实现要点
- 通过list_head进行目标结点定位( position(i) )
- 通过list_entry将list_head指针转换为目标结点指针
- 通过list_for_each实现int find(const T &e)函数
- 遍历函数中的next()和pre()需要考虑跳过头结点
编程实验
-
基于Linux内核链表的双向循环链表
#ifndef DUALCIRCULARLIST_H
#define DUALCIRCULARLIST_H
#include "DualLinkedList.h"
#include "LinuxList.h"
namespace KylinLib {
template< typename T>
class DualCircularList : public DualLinkedList<T>{
public:
DualCircularList(){
INIT_LIST_HEAD(&m_header);
}
bool insert(size_t index,const T &value){
auto node = new Node();
index = index%(this->m_size+1);
if(node!=nullptr){
node->value = value;
list_add_tail(&node->head,position(index)->next);
this->m_size++;
} else {
THROW_EXCEPTION(NoEnoughMemoryException,"No momery to insert new element...");
}
return true;
}
bool append(const T &value){
return insert(this->m_size,value);
}
bool remove(size_t index){
index = mod(index);
auto toDel = position(index)->next;
if(m_current==toDel)m_current=toDel->next;
list_del(toDel);
this->m_size--;
delete list_entry(toDel,Node,head);
return true;
}
virtual bool set(size_t index,const T &value) {
index = mod(index);
list_entry(position(index)->next,Node,head)->value = value;
return true;
}
virtual bool get(size_t index,T &value) const {
index = mod(index);
value = list_entry(position(index)->next,Node,head)->value;
return true;
}
virtual const T& at(size_t index) const {
index = mod(index);
return list_entry(position(index)->next,Node,head)->value;
}
virtual int find(const T &value) const {
int ret = -1;
int i = 0;
list_head *slider = nullptr;
list_for_each(slider,&m_header){
if(list_entry(slider,Node,head)->value == value){
ret = i;
break;
}
i++;
}
return ret;
}
void clear(){
while (this->m_size>0) {
remove(0);
}
}
bool begin(size_t index,size_t step = 1){
index = mod(index);
m_current = position(index)->next;
this->m_step = step;
return true;
}
bool end(){
return (m_current==nullptr)||(this->m_size==0);
}
T& current(){
if(!end())
return list_entry(m_current,Node,head)->value;
else {
THROW_EXCEPTION(InvalidOperationException,"No value at current position...");
}
}
bool next(){
size_t i = 0;
while ((i<this->m_step)&&!end()) {
if(m_current!=&m_header){
m_current = m_current->next;
i++;
} else {
m_current=m_current->next;
}
}
if(m_current==&m_header)
m_current = m_current->next;
return (i==this->m_step);
}
bool prev(){
size_t i = 0;
while ((i<this->m_step)&&!end()) {
if(m_current!=&m_header){
m_current = m_current->prev;
i++;
} else {
m_current=m_current->prev;
}
}
if(m_current==&m_header)
m_current = m_current->prev;
return (i==this->m_step);
}
~DualCircularList(){
clear();
}
protected:
list_head* position(size_t index)const{
auto ret = const_cast<list_head*>(&m_header);
for (size_t i=0;i<index;i++) {
ret = ret->next;
}
return ret;
}
size_t mod(size_t index) const{
return (this->m_size==0)?0:(index%this->m_size);
}
struct Node : public Object{
list_head head;
T value;
};
list_head m_header;
list_head *m_current = nullptr;
};
}
#endif // DUALCIRCULARLIST_H
小结
- Linux内核链表是带头结点的双向循环链表
- DualCircleList使用Linux内核链表进行内部实现
- DualCircleList在循环遍历时需要跳过头结点
- 将list_head指针转换为目标结点指针时,使用list_entry宏
思考题
-
下面代码中的pn1和pn2是否相等?为什么?
struct Node : public Object{
list_head head;
T value;
};
Node node;
list_head *ld = &node.head;
Node *pn1 = reinterpret_cast<Node*>(ld);
Node *pn2 = list_entry(ld,Node,head);